﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Drawing.Imaging;

namespace LowLevelGraphics.Filter
{
    /// <summary>
    /// Z = imabsdiff(X,Y) subtracts each element in array Y from the corresponding element in array X and
    /// returns the absolute difference in the corresponding element of the output array Z.
    /// X and Y are real, nonsparse numeric arrays with the same class and size.
    /// Z has the same class and size as X and Y.
    /// If X and Y are integer arrays, elements in the output that exceed the range of the integer type are truncated.
    /// 
    /// If X and Y are double arrays, you can use the expression abs(X-Y) instead of this function.
    /// 
    /// Note   This function may take advantage of hardware optimization for data types uint8, int16, and single to run faster. Hardware optimization requires that arrays X and Y are of the same size and class.
    /// 
    /// Examples
    /// 
    /// Calculate the absolute difference between two uint8 arrays. Note that the absolute value prevents negative values from being rounded to zero in the result, as they are with imsubtract.
    /// 
    /// X = uint8([ 255 10 75; 44 225 100]);
    /// Y = uint8([ 50 50 50; 50 50 50 ]);
    /// Z = imabsdiff(X,Y)
    /// 
    /// Z =
    /// 205    40    25
    /// 6   175    50
    /// Display the absolute difference between a filtered image and the original.
    /// I = imread('cameraman.tif');
    /// J = uint8(filter2(fspecial('gaussian'), I));
    /// K = imabsdiff(I,J);
    /// imshow(K,[]) % [] = scale data automatically    /// </summary>
    public class imabsdiff : BaseImageFilter, ITwoSourceFilter
    {
        protected delegate Color ColorCalculationDelegate(Color color1, Color color2);

        /// <summary>
        /// Initializes a new instance of the DeAnayglyph class.
        /// </summary>
        public imabsdiff()
        {
            M_PROCESSING_MODE = PROCESSING_MODE.GrayValue;
        }

        /// <summary>
        /// Initializes a new instance of the DeAnayglyph class.
        /// </summary>
        /// <param name="_anaglyph">The _anaglyph.</param>
        internal imabsdiff(imabsdiff _anaglyph)
        {
            M_PROCESSING_MODE = _anaglyph.M_PROCESSING_MODE;
        }

        /// <summary>
        /// Execute this Filter
        /// A Debug Statement is written if a concrete Filter is called
        /// </summary>
        /// <param name="_bitmap"></param>
        /// <returns></returns>
        public override UnsafeBitmap Execute(UnsafeBitmap _bitmap)
        {
            return Process((UnsafeBitmap)_bitmap.Clone(), _bitmap);
        }

        #region ITwoSourceFilter Members

        /// <summary>
        /// Processes the specified _BMP source left.
        /// </summary>
        /// <param name="_bmpSourceLeft">The _BMP source left.</param>
        /// <param name="_bmpSourceRight">The _BMP source right.</param>
        /// <returns></returns>
        public UnsafeBitmap Process(UnsafeBitmap _bmpSourceLeft, UnsafeBitmap _bmpSourceRight)
        {
            int x = 0;
            int y = 0;
            int nWidth = _bmpSourceLeft.Width;
            int nHeight = _bmpSourceRight.Width;
            ColorCalculationDelegate colorCalculation = null;

            switch (M_PROCESSING_MODE)
            {
                case PROCESSING_MODE.GrayColor:
                case PROCESSING_MODE.GrayValue:
                    colorCalculation = new ColorCalculationDelegate(GrayAbsDiff);
                    break;

                case PROCESSING_MODE.Color:
                    colorCalculation = new ColorCalculationDelegate(ColorAbsDiff);
                    break;
            }

            Color color1 = Color.Transparent;
            Color color2 = Color.Transparent;

            for (y = 0; y < nHeight; y++)
            {
                for (x = 0; x < nWidth; x++)
                {
                    color1 = _bmpSourceLeft.GetPixel(x,y);
                    color2 = _bmpSourceRight.GetPixel(x, y);
                    _bmpSourceLeft.SetPixel(x, y, colorCalculation(color1, color2));
                }
            }
            return _bmpSourceLeft;
        }

        /// <summary>
        /// Absolute Color Difference
        /// </summary>
        /// <param name="_color1"></param>
        /// <param name="_color2"></param>
        /// <returns></returns>
        protected Color ColorAbsDiff(Color _color1, Color _color2)
        {
            return Color.FromArgb(
                Math.Abs(_color1.R - _color2.R),
                Math.Abs(_color1.G - _color2.G),
                Math.Abs(_color1.B - _color2.B)
             );
        }

        /// <summary>
        /// Absolut Gray Difference
        /// </summary>
        /// <param name="_color1"></param>
        /// <param name="_color2"></param>
        /// <returns></returns>
        protected Color GrayAbsDiff(Color _color1, Color _color2)
        {
            int nAbs = Math.Abs(ColorFunctions.Gray(_color1)-ColorFunctions.Gray(_color2));
            return Color.FromArgb(nAbs, nAbs, nAbs);
        }

        #endregion

        /// <summary>
        /// This is used to clone a filter, each filter must implement it's own clone functionality
        /// </summary>
        /// <returns></returns>
        public override object Clone()
        {
            return new imabsdiff(this);
        }
    }
}
